const request = require('request');
const qs = require('querystring');
const fs = require('fs-extra');
const path = require('path');
const tesseract = require('tesseract.js');
const dotenv = require('dotenv');

const cwd = process.cwd();
/**
 * @name GET请求
 * @param {*} url
 * @param {*} options
 * @param {*} downloadPath 图片文件流下载到本地的路径
 */
function requestGet(url, options = {}, downloadPath = '') {
  let lastUrl = url;
  if (options.query) {
    url = url + '?' + qs.stringify(options.query);
  }
  return new Promise((resolve) => {
    const stach = request.get(lastUrl, { ...options }, (err, response, body) => {
      resolve(response);
    });
    if (downloadPath) {
      stach.pipe(fs.createWriteStream(downloadPath));
    }
  });
}

/**
 * @name POST请求
 * @param {*} url
 * @param {*} options
 * @param {*} downloadPath 图片文件流下载到本地的路径
 */
function requestPost(url, options = {}, downloadPath = '') {
  return new Promise((resolve) => {
    const stach = request.post(url, { ...options }, (err, response, body) => {
      resolve(response);
    });
    if (downloadPath) {
      stach.pipe(fs.createWriteStream(downloadPath));
    }
  });
}

/**
 * @name 本地图片识别
 * @param {*} file 本地文件地址
 * @returns {String} 图片识别结果
 */
async function recongizeCode(file) {
  try {
    const result = await tesseract.recognize(file);
    let text = '';
    if (result.data.text) {
      text = result.data.text.replace(/\s\S\n/, '');
    }
    return text;
  } catch (error) {
    LoginScripts.log('recongizeCode解析异常=====>', error);
    return '';
  }
}

/**
 * @name 更新env配置文件
 * @param {Object} newEnvConfig 更新env配置
 * @returns
 */
function updateEnvCookie(newEnvConfig = {}) {
  try {
    const envPath = path.resolve(cwd, '.env');
    const isExist = fs.pathExists(envPath);
    if (!isExist) {
      LoginScripts.log('.env文件不存在...');
      return false;
    }
    const envFile = fs.readFileSync(envPath);
    const envConfig = dotenv.parse(envFile);
    Object.assign(envConfig, newEnvConfig);
    let newEnvFile = '';
    for (const key in envConfig) {
      const value = envConfig[key];
      const str = `${key}=${value}`;
      newEnvFile += str + '\n';
    }
    if (newEnvFile) {
      fs.writeFileSync(envPath, newEnvFile, 'utf8');
    }
  } catch (error) {
    LoginScripts.log('更新env配置文件异常...', error);
  }
}
/**
 * @name 自动登录脚本
 */
class LoginScripts {
  constructor(prop) {
    this.LOGINCOUNT = 0; // * 第n次登录
    this.LOGIN_MAX_COUNT = 10; // * 第N次登录后不在进行自动登录
    this.USER = null;
    this.HOST = null;
    this.LOGIN_CONFIG = null;
    this.isCaptcha = false; // * 登录是否需要图形验证码
    this.initConfig(prop);
    this.main();
  }

  static log(info) {
    console.log(this, info);
  }

  initConfig(prop) {
    const { isCaptcha = false, USER, HOST = '', LOGIN_MAX_COUNT = 10, LOGIN_CONFIG = {} } = prop;
    /**
     * @name 需要自动登录的用户信息
     * @param {String} name 登录人的姓名
     * @param {String} username 登录人账号
     * @param {String} password 登录人的密码
     */
    this.USER = USER;
    this.HOST = HOST;
    this.LOGIN_MAX_COUNT = LOGIN_MAX_COUNT; // * 第N次登录后不在进行自动登录
    if (isCaptcha === true) {
      this.isCaptcha = {
        key: 'xxl_sso_captcha_code',
        url: HOST + '/sso-server/getCaptcha',
      };
    }
    if (isCaptcha && typeof isCaptcha === 'object') {
      this.isCaptcha = Object.assign(
        { key: 'xxl_sso_captcha_code', url: HOST + '/sso-server/getCaptcha' },
        isCaptcha,
      );
    }
    this.LOGIN_CONFIG = Object.assign(
      {
        method: 'GET',
        loginKey: 'xxl_sso_sessionid',
        usernameKey: 'username',
        passwordKey: 'password',
        captchaKey: 'captchaCode',
      },
      LOGIN_CONFIG,
    );
  }

  /**
   * @name 获取登录接口地址
   * @param {String} username 用户名
   * @param {String} password 密码
   */
  async getLoginUrl({ username, password }) {
    let xxl_sso_captcha_code = '';
    let captchaCode = '';
    const { isCaptcha, LOGIN_CONFIG } = this;
    const {
      method = 'GET',
      url: loginUrl,
      usernameKey = 'username',
      passwordKey = 'password',
      captchaKey = 'captchaCode',
    } = LOGIN_CONFIG;
    let data = {
      [usernameKey]: username,
      [passwordKey]: password,
    };
    let url = loginUrl;
    try {
      if (isCaptcha) {
        const captchPath = path.resolve(__dirname, 'captch.png');
        const result = await requestGet(isCaptcha.url, {}, captchPath);
        const setCookie = result.headers['set-cookie'][0];
        xxl_sso_captcha_code = qs.parse(setCookie, ';')[isCaptcha.key];
        LoginScripts.log(`图形验证码后端标识=========>${xxl_sso_captcha_code}`);
        captchaCode = await recongizeCode(captchPath);
        LoginScripts.log(`识别图形验证码结果=========>${captchaCode}`);
        data[captchaKey] = captchaCode ? captchaCode.replace('\n', '') : '';
        fs.removeSync(captchPath);
      }
      if (method.toLowerCase() === 'get') {
        url = `${url}?${usernameKey}=${username}&${passwordKey}=${password}&${captchaKey}=${captchaCode}`;
      }
      LoginScripts.log(`登录接口请求地址是=========>${url}`);
    } catch (error) {
      LoginScripts.log('获取登录接口地址异常...', error);
    }
    return {
      url: url,
      method: method,
      data: data,
      xxl_sso_captcha_code: xxl_sso_captcha_code,
    };
  }

  /**
   * @name 从登录请求实例中获取xxl_sso_sessionid
   * @param {*} result 登录接口请求实例
   */
  getXxlSsoSessionid(result) {
    const { LOGIN_CONFIG } = this;
    const { loginKey = '' } = LOGIN_CONFIG;
    const resultBody = JSON.parse(result.body);
    const redirectUrl = resultBody?.data?.redirectUrl ?? '';
    const newRedirectUrl = redirectUrl.substr(redirectUrl.indexOf('?')).replace('?', '');
    const url_xxl_sso_sessionid = qs.parse(newRedirectUrl)[loginKey]; // * url上获取到的 xxl_sso_sessionid
    const setCookie = result.headers['set-cookie'] ?? [];
    const cookie_xxl_sso_sessionid = setCookie.filter((t) => t.indexOf(loginKey) !== -1)[0] ?? '';
    const cookie_xxl_sso_sessionid_parse = qs.parse(cookie_xxl_sso_sessionid, ';') ?? {};
    const last_cookie_xxl_sso_sessionid = cookie_xxl_sso_sessionid_parse[loginKey] ?? ''; // * 请求头中获取 xxl_sso_sessionid
    return url_xxl_sso_sessionid || last_cookie_xxl_sso_sessionid;
  }

  async main() {
    try {
      const { LOGINCOUNT, LOGIN_MAX_COUNT, USER, isCaptcha, LOGIN_CONFIG } = this;
      const { loginKey = '' } = LOGIN_CONFIG;
      if (LOGINCOUNT > LOGIN_MAX_COUNT) {
        return false;
      }
      this.LOGINCOUNT = LOGINCOUNT + 1;
      LoginScripts.log(`开始第${this.LOGINCOUNT}次登录==========>`);
      const { url: loginUrl, xxl_sso_captcha_code, method = 'GET', data } = await this.getLoginUrl(
        USER,
      );
      let result = null;
      let headers = {};
      if (isCaptcha) {
        headers.Cookie = `${isCaptcha.key}=${xxl_sso_captcha_code}`;
      }
      if (method.toLowerCase() === 'get') {
        result = await requestGet(loginUrl, {
          headers: headers,
        });
      } else if (method.toLowerCase() === 'post') {
        result = await requestPost(loginUrl, {
          headers: headers,
          data: data,
        });
      }
      const resultBody = JSON.parse(result.body);
      if (resultBody.code !== 0) {
        LoginScripts.log(resultBody.message ?? result.body);
        this.main();
        return false;
      }
      const xxl_sso_sessionid = this.getXxlSsoSessionid(result);

      LoginScripts.log(xxl_sso_sessionid);
      LoginScripts.log(`用户登录成功的${loginKey}=========>${xxl_sso_sessionid}`);
      if (xxl_sso_sessionid) {
        updateEnvCookie({ Cookie: `${loginKey}=${xxl_sso_sessionid}` });
      }
    } catch (error) {
      LoginScripts.log('用户自动登录异常', error);
    }
  }
}

module.exports = {
  LoginScripts,
};
